#include<stdio.h>
#include<stdlib.h>
#include<sys/shm.h>  // shared memory
#include<sys/sem.h>  // semaphore
#include<sys/msg.h>  // message queue
#include<string.h>   // memcpy

// 消息队列结构
struct msg_form
{
	long mtype;
	char mtext;
};

// 联合体，用于semctl初始化
union semun
{
	//for SETVAL
	int              val;
	struct semid_ds *buf;
	unsigned short  *array;
};

// 初始化信号量
int init_sem(int sem_id, int value)
{
	union semun tmp;
	tmp.val = value;
	if (semctl(sem_id, 0, SETVAL, tmp) == -1)
	{
		perror("Init Semaphore Error");
		return -1;
	}
	return 0;
}

// P操作:
// 若信号量值为1，获取资源并将信号量值-1 
// 若信号量值为0，进程挂起等待
int sem_p(int sem_id)
{
	struct sembuf sbuf;
	//待操作的信号量的序号
	sbuf.sem_num = 0;
	//P操作
	sbuf.sem_op = -1;
	sbuf.sem_flg = SEM_UNDO;

	if (semop(sem_id, &sbuf, 1) == -1)
	{
		perror("P operation Error");
		return -1;
	}
	return 0;
}

// V操作：
// 释放资源并将信号量值+1
// 如果有进程正在挂起等待，则唤醒它们
int sem_v(int sem_id)
{
	struct sembuf sbuf;
	//待操作的信号量的序号
	sbuf.sem_num = 0;
	//V操作
	sbuf.sem_op = 1;
	sbuf.sem_flg = SEM_UNDO;

	if (semop(sem_id, &sbuf, 1) == -1)
	{
		perror("V operation Error");
		return -1;
	}
	return 0;
}

// 删除信号量集
int del_sem(int sem_id)
{
	union semun tmp;
	if (semctl(sem_id, 0, IPC_RMID, tmp) == -1)
	{
		perror("Delete Semaphore Error");
		return -1;
	}
	return 0;
}

// 创建一个信号量集
int creat_sem(key_t key)
{
	int sem_id;
	if ((sem_id = semget(key, 1, IPC_CREAT | 0666)) == -1)
	{
		perror("semget error");
		exit(-1);
	}
	//初始化信号量，初值设为1，表示资源未占用
	init_sem(sem_id, 1);
	return sem_id;
}


int main()
{
	key_t key;
	int shmid, semid, msqid;
	char *shm;
	char data[] = "this is server";
	//用于删除共享内存
	struct shmid_ds buf1;
	//用于删除消息队列
	struct msqid_ds buf2;
	//消息队列用于通知对方更新了共享内存
	struct msg_form msg;

	// 获取key值
	if ((key = ftok(".", 'z')) < 0)
	{
		perror("ftok error");
		exit(1);
	}

	// 创建共享内存
	if ((shmid = shmget(key, 1024, IPC_CREAT | 0666)) == -1)
	{
		perror("Create Shared Memory Error");
		exit(1);
	}

	// 连接共享内存
	shm = (char*)shmat(shmid, 0, 0);
	if (shm == (void*)-1)
	{
		perror("Attach Shared Memory Error");
		exit(1);
	}

	// 创建消息队列
	if ((msqid = msgget(key, IPC_CREAT | 0777)) == -1)
	{
		perror("msgget error");
		exit(1);
	}

	// 创建信号量
	// semid = creat_sem(key);
	if ((semid = semget(key, 1, IPC_CREAT | 0666)) == -1)
	{
		perror("semget error");
		exit(-1);
	}

	//初始化信号量，初值设为1，表示资源未占用
	init_sem(semid, 1);

	// 读数据
	while (1)
	{
		//读取类型为888的消息
		msgrcv(msqid, &msg, 1, 888, 0);
		//quit - 跳出循环
		if (msg.mtext == 'q')
			break;
		//read - 读共享内存
		if (msg.mtext == 'r')
		{
			sem_p(semid);
			printf("%s\n", shm);
			sem_v(semid);
		}
	}

	// 断开连接
	shmdt(shm);

	/*删除共享内存、消息队列、信号量*/
	shmctl(shmid, IPC_RMID, &buf1);
	msgctl(msqid, IPC_RMID, &buf2);
	del_sem(semid);
	return 0;
}
